package com.breeze.breezelive.record;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.util.Log;

import com.breeze.breezelive.record.bean.MutexData;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;

public class AudioRecordThread extends Thread {
    private static final String TAG = "AudioRecordThread";
    private static final int TIMEOUT_MS = 10000; // 10s
    private static final int SAMPLE_RATE_HZ = 24000;
    private static final int BIT_RATE = 64000;
    private static final int CHANNEL_COUNT = 1;

    private WeakReference<MediaMuxerThread> mMutex;
    private boolean isRecording;
    private MediaCodec mMediaCodec;
    private AudioRecord mAudioRecorder;
    private int mMinBufferSize;
    private long prevOutputPTS;

    public AudioRecordThread(MediaMuxerThread mediaMutexThread) {
        this.mMutex = new WeakReference<>(mediaMutexThread);
    }

    private void initCodec() {
        try {
            // 编码为AAC格式
            MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, SAMPLE_RATE_HZ, CHANNEL_COUNT);
            format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
            mMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
            mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            mMediaCodec.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void initRecorder() {
        mMinBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE_HZ, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
        mAudioRecorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, SAMPLE_RATE_HZ, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, mMinBufferSize);
        mAudioRecorder.startRecording();
    }

    @Override
    public void run() {
        byte[] bufferBytes = new byte[mMinBufferSize];
        int len = 0;
        while (isRecording) {
            len = mAudioRecorder.read(bufferBytes, 0, mMinBufferSize);
            if (len > 0) {
                encode(bufferBytes, len, getPTS());
            }
        }
        release();
    }

    private void encode(byte[] bufferBytes, final int len, final long pts) {
        int inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_MS);
        if (inputBufferIndex >= 0) {
            ByteBuffer inputBuffer = mMediaCodec.getInputBuffer(inputBufferIndex);
            inputBuffer.clear();
            Log.i(TAG, "encode: " + bufferBytes.length + ", " + inputBuffer.capacity());
            inputBuffer.put(bufferBytes);
            if (len <= 0) {
                mMediaCodec.queueInputBuffer(inputBufferIndex, 0, 0, pts, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                Log.i(TAG, "send BUFFER_FLAG_END_OF_STREAM");
            } else {
                mMediaCodec.queueInputBuffer(inputBufferIndex, 0, len, pts, 0);
            }
        }

        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_MS);
        if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
            Log.e(TAG, "run: INFO_OUTPUT_FORMAT_CHANGED");
            MediaMuxerThread mediaMutex = mMutex.get();
            if (mediaMutex != null && !mediaMutex.isAudioTrackExist()) {
                mediaMutex.addAudioTrack(mMediaCodec.getOutputFormat());
            }
        }

        while (outputBufferIndex >= 0) {
            ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex);
            if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                Log.w(TAG, "audio run: BUFFER_FLAG_CODEC_CONFIG");
                bufferInfo.size = 0;
            }
            if (bufferInfo.size > 0) {
                MediaMuxerThread mediaMuxer = mMutex.get();
                if (mediaMuxer != null) {
                    byte[] outData = new byte[bufferInfo.size];
                    outputBuffer.get(outData);
                    outputBuffer.position(bufferInfo.offset);
                    outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
                    bufferInfo.presentationTimeUs = getPTS();
                    mediaMuxer.putMutexData(new MutexData(false, outData, bufferInfo));
                    prevOutputPTS = bufferInfo.presentationTimeUs;
                    Log.i(TAG, "encode: inputPTS=" + pts + ", prevOutputPTS=" + prevOutputPTS);
                }
            }
            mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
            bufferInfo = new MediaCodec.BufferInfo();
            outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_MS);
        }
    }

    public void begin() {
        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
        prevOutputPTS = 0;
        isRecording = true;
        start();
    }

    public void end() {
        isRecording = false;
    }

    public void prepare() {
        initCodec();
        initRecorder();
    }

    private long getPTS() {
        long result = System.nanoTime() / 1000L;
        return result < prevOutputPTS ? prevOutputPTS : result;
    }

    private void release() {
        if (mAudioRecorder != null) {
            mAudioRecorder.stop();
            mAudioRecorder.release();
            mAudioRecorder = null;
        }
        if (mMediaCodec != null) {
            mMediaCodec.stop();
            mMediaCodec.release();
            mMediaCodec = null;
        }
    }
}
